#pragma once

#include "BackupFSFileNode.h"
#include <memory>
#include <mutex>

#include <optional>
#include <iostream>
#include <set>
#include <unordered_map>
#include "BackupFSLRU.h"
/*
Used to store file lists and maintain various file operations, 
such as adding, deleting, renaming, etc.
*/
namespace BackupFS
{
	class BackupFSFileNodes
	{
	public:
		BackupFSFileNodes();
		~BackupFSFileNodes();

		void AddRoot(bool add_to_db);
		// Add a new filenode to the filesystem hierarchy.
		// The file will directly be visible on the filesystem.
		// An already processed GetStreamNames can optional be provided
		NTSTATUS Add(const std::shared_ptr<BackupFSFileNode>& filenode,
				std::optional<std::pair<std::wstring, std::wstring>> stream_names);

		// Return the filenode linked to the filename if present.
		std::shared_ptr<BackupFSFileNode> Find(const std::wstring& filename);

		// Return all filenode of the directory scope give in param.
		std::set<std::shared_ptr<BackupFSFileNode>> ListFolder(const std::wstring& filename);

		// Remove filenode from the filesystem hierarchy.
		// If the filenode has alternated streams attached, they will also be removed.
		// If the filenode is a directory not empty, all sub filenode will be removed
		// recursively.
		void Remove(const std::wstring& filename);
		void Remove(const std::shared_ptr<BackupFSFileNode>& filenode);
		// Stop the nodes of the file system
		void Stop();
		void SetFlushFrequency(int frequency) { _FlushFrequency = frequency; }
		void SetCacheNumber(ULONGLONG num) { _NodesCacheNumber = num; }
		// Move the current filenode position to the new one in the filesystem
		// hierarchy.
		NTSTATUS Move(const std::wstring& old_filename,
			const std::wstring& new_filename, BOOL replace_if_existing);

		// Help - return a pair containing for example for \foo:bar
		// first: filename: foo
		// second: alternated stream name: bar
		// If the filename do not contain an alternated stream, second is empty.
		static std::pair<std::wstring, std::wstring> GetStreamNames(
			std::wstring real_filename);
		void FlushFS();

	private:
		void _AddRoot(bool add_to_db);
		bool _InsertToDb(std::shared_ptr<BackupFSFileNode> node);
		void _UpdateNodeLRU(const std::wstring& filename);
		void _ReleaseCache();
	private:
		// Global FS FileIndex count.
		// Note: Alternated stream and main stream share the same FileIndex.
		std::atomic<LONGLONG> _FsFileIndexCount = 1;

		// Mutex need to be aquired when using fileNodes / directoryPaths.
		std::recursive_mutex _FilesNodesMutex;
		// Global map of filename / filenode for all the filesystem.
		std::unordered_map<std::wstring, std::shared_ptr<BackupFSFileNode>> _FileNodes;
		// Directory map of directoryname / sub filenodes in the scope.
		// A directory \foo with 2 files bar and coco will have one entry:
		// first: foo - second: set filenode { bar, coco }
		std::unordered_map<std::wstring, std::set<std::shared_ptr<BackupFSFileNode>>>_DirectoryPaths;
		BackupFSLRU<std::wstring,LONGLONG> _NodesCache;
		bool _StopFS = false;
		std::recursive_mutex _StopFSMutex;
		std::atomic<int> _FlushFrequency = 3;
		ULONGLONG _NodesCacheNumber = 10000000;
		std::atomic<int> _FlushNow = 0;

	};
}  // namespace BackupFS
